#include <stdlib.h>

#include "ScoreFunctions.h"

#include "../Utils/Utils.h"
#include "../LinearComputation/Vector.h"
#include "../SparceMatrix/sparse_matrix.h"
#include "../PowerMethod/Computations.h"

/**
 *  comment: multProduct is the result of multipling modularity matrix B by s (groupVec)
 * 
 */
void InitScore(const elem* groupVec, const elem* multProduct, const elem* degVec,
		elem Minverse, elem* score, unsigned size)
{
	unsigned i = 0;
	for (i = 0; i < size; i++)
		score[i] = -2 * (groupVec[i] * multProduct[i] + degVec[i] * degVec[i] * Minverse);
}


/*
 * @coment : we supose that the vertix was already moved. 
 */
void UpdateScore(const elem* groupVec, const elem* modularityMatrix, elem* score,
		unsigned vertix, unsigned size)
{
	unsigned i, matrixIter;
	for (i = 0, matrixIter = vertix; i < size; i++, matrixIter += size)
		if (i == vertix)
			score[i] = - score[i];
		else
			score[i] -= 4 * groupVec[i] * groupVec[vertix] * modularityMatrix[matrixIter];
}

void MoveBetweenGroups (elem* groupVec, const elem* modularityMatrix, elem* score,
		const elem* degVec, bool* moved, unsigned vertix, unsigned size)
{
	groupVec[vertix] = -groupVec[vertix];
	moved[vertix] = TRUE;
	UpdateScore(groupVec, modularityMatrix, score, vertix, size);
}

/**
 * @param groupVec - vector of +-1
 * 		modularityMat - matrix B (not TopModularityMatrix!!!!!!!)
 */
elem* ImproveDiv (elem* groupVec, const sparse_matrix_lst* adjMatrix,
		elem* degVec, elem MInverse, const elem* modularityMat, elem* globalDeltaQ)
{
	elem deltaQ = 0, *score = NULL, *xVec = NULL, *improve = NULL;
	bool* moved = NULL;
	unsigned maxIndex = 0, i = 0, * indices = NULL;
	const unsigned size = adjMatrix->n;
	
	NAssert(MemoryAssert(3, moved = (bool*)malloc(sizeof(bool) * size), 
				score = (elem*)malloc(sizeof(elem) * size),
				improve = (elem*)malloc(sizeof(elem) * size),
				indices = (unsigned*)malloc(sizeof(unsigned) * size)));
	
	do
	{
		InitVectorWithBool(moved, FALSE, size);

		xVec = ModularityMult(adjMatrix, degVec, MInverse, NULL, 0, groupVec);
		InitScore(groupVec, xVec, degVec, MInverse, score, size);
		
		for (i = 0; i < size; i++)
		{
			maxIndex = FindMaxIndexUnmoved (score, moved, size);
			indices[i] = maxIndex;
			improve[i] = score[maxIndex] + ((i == 0) ? 0 : improve[i - 1]);
			
			MoveBetweenGroups(groupVec, modularityMat, score, degVec, moved, maxIndex, size);
		}
		
		maxIndex = FindMaxIndex(improve, size);
		if ((deltaQ = ((maxIndex == size - 1) ? 0 : improve[maxIndex])) > 0)
			for (i = size - 1; i > maxIndex; i--)
				groupVec[indices[i]] = -groupVec[indices[i]];
		
		*globalDeltaQ += (deltaQ > 0) ? deltaQ : 0;
	}
	
	while(deltaQ > 0);
	
	return groupVec;
}
